<?php
/**
 * MongoDb封装实现常用操作
 * @file MongoImpl.php
 * @author zhanghaozheng@jiehun.com.cn
 * @date 2013.12.03 
 *
 **/
class MongoImpl{ 

	private $conf; 
	private $connection; 
	private $save;
	private $db; 
	private $connect_str; 
	private $host; 
	private $port; 
	private $user; 
	private $pass; 
	private $dbname; 
	private $selects = array(); 
	private $wheres = array(); 
	private $sorts = array(); 
	private $limit = 999999; 
	private $offset = 0; 
	private $timeout = 200; 
	private $table;
	private $batchCount = 1000;//批量数量

	public function __construct($dbName,$conf) { 
		if(!class_exists('Mongo'))
		{ 
			throw new Exception('mongodb extension has not been installed or enabled');
		} 
		$this->conf = $conf; 
		$this->conf['database'] = $dbName;
	} 

	/**
	 *释放连接资源
	 */
	function __destruct()
	{ 
		if($this->connection) 
		{ 
			$this->connection->close(); 
		} 
	} 

	/**
	* 检查表名
	*/
	private function checkDbTable() {
		if(empty($this->table)) {
			throw new Exception( "mongo_db.TableEmpty" );
		}
	}

	/**
	 *执行链接
	 */
	private function connect() { 
		$this->checkDbTable();
		$this->connect_str(); 
		$options = array('connect'=>true,'connectTimeoutMS'=>$this->timeout*1000); 
		$this->connection = new Mongo($this->connect_str, $options); 
		$this->db = $this->connection->{$this->dbname}; 
		return($this); 
	} 
	/**
	 *获取mongo链接字符串
	 */
	private function connect_str() { 
		$this->host = trim($this->conf['ip']); 
		$this->port = trim($this->conf['port']); 
		$this->user = trim($this->conf['user']); 
		$this->pass = trim($this->conf['pass']); 
		$this->dbname = trim($this->conf['database']); 

		$connect_str = "mongodb://"; 
		if (empty($this->host))
		{ 
			throw new Exception(' connect mongodb host error');
		} 
		if(empty($this->dbname))
		{ 
			throw new Exception(' connect mongodb database not found');
		}
		if(!empty($this->user) && !empty($this->pass))
		{ 
			$connect_str .= "{$this->user}:{$this->pass}@"; 
		}
		if(isset($this->port) && !empty($this->port))
		{ 
			$connect_str .= "{$this->host}:{$this->port}"; 
		}else
		{ 
			$connect_str .= "{$this->host}"; 
		}
		$this->connect_str = trim($connect_str); 
	} 

	/**
	 *设置表名
	 */
	public function table($tableName)
	{
		$this->table = $tableName;
		return ($this);
	}



	/*
	 * 设置查询查询字段
	 *@params $includes 字段
	 *@params $excludes 不包含的字段
	 */
	public function field($includes=array(),$excludes = array('_id'))
	{ 
		if (!is_array($includes)) { 
			$includes = array(); 
		}  
		if (!empty($includes)) { 
			foreach ($includes as $col)
			{ 
				$this->selects[$col] = 1; 
			} 
		} 
		if (!empty($excludes)) { 
			foreach ($excludes as $col)
			{ 
				$this->selects[$col] = 0; 
			} 
		} 
		return($this); 
	} 

	/*
	 * 设置where条件
	 * @params $wheres array(key=>value)
	 */
	public function where($wheres = array())
	{ 
		foreach ($wheres as $wh => $val)
		{ 
			$this->wheres[$wh] = $val; 
		}
		return($this); 
	} 

	/*
	 * 设置in条件
	 @params $field 字段名
	 @params $in 取值数组
	 */
	public function in($field,$in=array())
	{ 
		$this->where_init($field); 
		$this->wheres[$field]['$in'] = $in; 
		return($this); 
	} 

	/*
	 * 设置not in条件
	 @params $field 字段名
	 @params $in 取值数组
	 */
	public function not_in($field , $in = array()) { 
		$this->where_init($field); 
		$this->wheres[$field]['$nin'] = $in; 
		return($this); 
	} 


	/**
	 *设置区间条件 
	 *@params $field 字段名
	 *@params $min 左区间
	 *@params $max 右区间
	 */
	public function between($field,$min=false,$max=false) { 
		$this->where_init($field); 
		$min!==false && $this->wheres[$field]['$gte'] = $min; 
		$max!==false && $this->wheres[$field]['$lte'] = $max; 
		return($this); 
	} 


	/**
	 *设置不等与条件
	 *@params $field 字段名
	 *@params $x 值
	 */
	public function not_equal($field,$x) { 
		$this->where_init($field); 
		$this->wheres[$field]['$ne'] = $x; 
		return($this); 
	} 

	/**
	 *设置or查询条件
	 *@params $field 字段名
	 @params $values 取值
	 */
	public function where_or($field,$values)
	{ 
		$this->wheres['$or'][][$field] = $values; 
		return($this); 

	} 



	/**
	 *设置排序字段
	 *@params $field 字段名
	 @params $asc true 升序 false 降序
	 */
	public function order($order,$asc=false)
	{
		$this->sorts[$order] = $asc===true?1:-1; 
		return ($this);
	}

	/*
	 * 设置查询limit,必须整数
	 */
	public function limit($start,$limit) 
	{
		if(is_numeric($start) && is_numeric($limit))
		{
			$this->offset = intval($start);
			$this->limit = intval($limit);
		}
		return ($this);
	}
		/**
		 *执行查询
		 */
		public function get()
		{
			$this->connect();
			$results = array(); 
			$documents = $this->db->{$this->table}->find($this->wheres, $this->selects)->limit((int) $this->limit)->skip((int) $this->offset)->sort($this->sorts); 
			$returns = array(); 
			foreach ($documents as $doc)
			{
				$returns[] = $doc; 
			}
			$this->clear();
			return($returns); 
		} 

		/**
		 * 保存属性
		 */
		public function save($arr_save) {
			return $this->saveBody($arr_save);
		}

		/**
		 * 设置更新字段数组
		 * @params $arr_save array  key=>value格式
		 */
		public function saveBody($arr_save){
			if(!is_array($arr_save ))
			{
				throw new Exception( "mongo_db.SaveBodyParam:$arr_save NotArray" );
			}
			$this->save = $arr_save;
			return $this;
		}

		/**
		 *获取数量
		 */
		public function count() { 
			$this->connect();
			$count = $this->db->{$this->table}->find($this->wheres)->count(); 
			//$this->clear(); 
			return($count); 
		} 

		/**
		 *插入数据
		 */
		public function insert()
		{ 
			$this->connect();
			$this->db->{$this->table}->insert($this->save, array('fsync' => TRUE));  
			$this->clear(); 
			return true; 
		} 

		/**
		*批量插入
		*/
		public function batchInsert($attrs)
		{
			$attrsAr = array_chunk($attrs,$this->batchCount,true);
			$this->connect();
			foreach($attrsAr as $onar)
			{
				$this->db->{$this->table}->batchInsert($onar, array('continueOnError' => true));  
			}
			$this->clear(); 
			return true; 
		}


		/**
		 *更改数据
		 */
		public function update()
		{ 
			$this->connect();
			$this->db->{$this->table}->update($this->wheres, array('$set' => $this->save), array('fsync' => TRUE, 'multiple' => TRUE)); 
			$this->clear(); 
			return(TRUE); 

		} 
		
		/**
		*创建索引
		*@param $key array('column1'=>-1//倒序索引 'column2'=>1正序索引)
		*/
		public function add_index($keys=array(),$options=array())
		{ 
			if($keys && is_array($keys))
			{
				$this->connect();
				foreach ($keys as $col => $val) 
				{ 
					if ($val == -1 || $val === FALSE || strtolower($val) == 'desc') { 
						$keys[$col] = -1; 
					} else { 
						$keys[$col] = 1; 
					} 
				}
				if ($this->db->{$this->table}->ensureIndex($keys, $options) == TRUE) 
				{ 
					$this->clear(); 
					return($this); 
				} else
				{ 
					throw new Exception('mongo.ensureIndex error');
				} 
			}
		} 


		/**
		 *删除整表
		 */
		public function drop()
		{ 
			$this->connect();
			$result = $this->db->{$this->table}->drop(); 
			return true;
		} 

		/**
		 *执行删除
		 */
		public function delete()
		{ 
			$this->connect();
			$result = $this->db->{$this->table}->remove($this->wheres, array('fsync' => TRUE, 'justOne' =>FALSE)); 
			if(isset($result['n']) && $result['n']>0)
			{
				return intval($result['n']);
			}
			return false;
		} 


		/**
		 *重置table所有属性
		 */
		private function clear() { 
			$this->selects = array(); 
			$this->wheres = array(); 
			$this->limit = null; 
			$this->offset = null; 
			$this->sorts = array(); 
			$this->table = null;
		} 

		/**
		 *设置where参数
		 */
		private function where_init($param) { 
			if (!isset($this->wheres[$param])) { 
				$this->wheres[$param] = array(); 
			} 
		} 
	} 
